import random
import numpy as np
import config
import matplotlib.pyplot as plt
from sklearn.preprocessing import OneHotEncoder
from scipy import stats


class UserModel:

    def __init__(self):
        self.Encoder = OneHotEncoder()
        # 指定用户总体数量
        self.SampleNumber = config.SampleNumber
        # 窗口内单次活动时长
        self.DurationSingleActivity = np.random.normal(1, 1.82) * config.DurationSingleActivityK * np.abs(
            np.random.normal(0, config.
                             DurationSingleActivityVar * np.abs(np.random.normal(1, .61)),
                             config.SampleNumber))
        # 窗口活动间隔时长
        self.WindowActivityDuration = np.random.normal(1, 1.852) * 0.7 * np.abs(np.random.normal(
            np.random.uniform(1, 5), np.abs(np.random.normal(1.4, .842)) * 1.5, config.SampleNumber))
        # 窗体内活动持续时长
        self.FormActivityDuration = np.random.normal(1, 1.835) * 0.26 * np.square(
            np.random.normal(1, np.abs(np.random.normal(1.5, .7517)) * 2.5, config.SampleNumber))
        # 窗体活动间隔次数 ----------------------[]k
        # self.ActivityInterval = self.Encoder.fit_transform(
        #     np.random.poisson(np.random.normal(1,.1)*config.LambdaParam,config.SampleNumber).reshape(-1,1)).toarray().T
        self.__ActivityInterval__ = None
        self.ActivityInterval = np.random.poisson(np.abs(np.random.normal(1, .1)) * config.LambdaParam, config.SampleNumber)
        # 鼠标键盘活动时长
        self.InputDeviceDuration = np.abs(
            np.random.normal(np.random.uniform(100, 1000), np.abs(np.random.normal(1, .71)) * 3, config.SampleNumber))
        # 窗口名 ----------------------------[]k
        self.__FormNameTag__ = None
        self.FormNameTag = np.floor(7 * np.random.exponential(1 / config.ExponetialRate, config.SampleNumber)).astype(
            int)
        # 活跃窗口数量 -------------------------[]k
        self.__ActiveFormNumber__ = None
        self.ActiveFormNumber = np.floor(np.random.normal(1, .731) * 3 * np.random.exponential(
            np.abs(np.random.normal(.95, .753)) / config.ExponetialRate, config.SampleNumber)).astype(int)
        # 最大化最小化窗口数量 ----------------------[]k
        self.__Max2MinWindowNumber__ = None
        self.Max2MinWindowNumber = np.floor(np.abs(np.random.normal(1, .1) * 5 * np.random.exponential(
            np.abs(np.random.normal(.99, .75)) / config.ExponetialRate, config.SampleNumber))).astype(int)
        # 窗口自定义情况
        self.p = np.random.random()
        self.SelfSetting = [1 if i > self.p else 0 for i in np.random.random(config.SampleNumber)]
        # 软件切换顺序
        pass
        # 窗口切换耗时
        pass
        # 窗口打开的星期和时间
        pass
        self.Map = None

    def dsac(self):
        plt.hist(self.DurationSingleActivity, bins=30, density=True)
        plt.title('Normal Distribution')
        plt.xlabel('Value')
        plt.ylabel('Probability Density')
        plt.show()

    def EachOneHotCoding(self, *anothers):
        MulActivityInterval = self.ActivityInterval
        MulFormNameTag = self.FormNameTag
        MulActiveFormNumber = self.ActiveFormNumber
        MulMax2MinWindowNumber = self.Max2MinWindowNumber
        for another in anothers:
            MulActivityInterval = np.append(MulActivityInterval, another.ActivityInterval)
            MulFormNameTag = np.append(MulFormNameTag, another.FormNameTag)
            MulActiveFormNumber = np.append(MulActiveFormNumber, another.ActiveFormNumber)
            MulMax2MinWindowNumber = np.append(MulMax2MinWindowNumber, another.Max2MinWindowNumber)
        MulActivityInterval = self.Encoder.fit_transform(MulActivityInterval.reshape(-1, 1)).toarray()
        MulFormNameTag = self.Encoder.fit_transform(MulFormNameTag.reshape(-1, 1)).toarray()
        MulActiveFormNumber = self.Encoder.fit_transform(MulActiveFormNumber.reshape(-1, 1)).toarray()
        MulMax2MinWindowNumber = self.Encoder.fit_transform(MulMax2MinWindowNumber.reshape(-1, 1)).toarray()
        self.__ActivityInterval__ = MulActivityInterval[:config.SampleNumber]
        self.__FormNameTag__ = MulFormNameTag[:config.SampleNumber]
        self.__ActiveFormNumber__ = MulActiveFormNumber[:config.SampleNumber]
        self.__Max2MinWindowNumber__ = MulMax2MinWindowNumber[:config.SampleNumber]
        self.Map = np.concatenate((
            np.array([self.DurationSingleActivity]),
            np.array([self.WindowActivityDuration]),
            np.array([self.FormActivityDuration]),
            self.__ActivityInterval__.T,
            np.array([self.InputDeviceDuration]),
            self.__FormNameTag__.T,
            self.__ActiveFormNumber__.T,
            self.__Max2MinWindowNumber__.T,
            np.array([self.SelfSetting])
        ), axis=0).T
        self.Map = self.Map / np.array(
            [i if i != 0 else 1 for i in np.linalg.norm(self.Map, axis=0)[:, np.newaxis].reshape(-1)])
        for i, another in enumerate(anothers):
            another.__ActivityInterval__ = MulActivityInterval[
                                           (i + 1) * config.SampleNumber:(i + 2) * config.SampleNumber]
            another.__FormNameTag__ = MulFormNameTag[(i + 1) * config.SampleNumber:(i + 2) * config.SampleNumber]
            another.__ActiveFormNumber__ = MulActiveFormNumber[
                                           (i + 1) * config.SampleNumber:(i + 2) * config.SampleNumber]
            another.__Max2MinWindowNumber__ = MulMax2MinWindowNumber[
                                              (i + 1) * config.SampleNumber:(i + 2) * config.SampleNumber]
            another.Map = np.concatenate((
                np.array([another.DurationSingleActivity]),
                np.array([another.WindowActivityDuration]),
                np.array([another.FormActivityDuration]),
                another.__ActivityInterval__.T,
                np.array([another.InputDeviceDuration]),
                another.__FormNameTag__.T,
                another.__ActiveFormNumber__.T,
                another.__Max2MinWindowNumber__.T,
                np.array([another.SelfSetting])
            ), axis=0).T
            another.Map = another.Map / np.array(
                [i if i != 0 else 1 for i in np.linalg.norm(another.Map, axis=0)[:, np.newaxis].reshape(-1)])

    def TransformKS(self, another):
        if self.Map is None or another.Map is None:
            self.EachOneHotCoding(another)
        sampleUserFirst = np.array([np.sum(random.sample(list(self.Map), config.NNN), axis=0)])
        sampleUserSecond = np.array([np.sum(random.sample(list(another.Map), config.NNN), axis=0)])
        for i in range(config.NUM - 1):
            # print(np.sum(random.sample(list(self.Map),config.NNN),axis=0).shape)
            # print(sampleUserSecond.shape)
            sampleUserFirst = np.vstack((sampleUserFirst, np.sum(random.sample(list(self.Map), config.NNN), axis=0)))
            sampleUserSecond = np.vstack(
                (sampleUserSecond, np.sum(random.sample(list(another.Map), config.NNN), axis=0)))
        sampleUserSecond = sampleUserSecond.T
        sampleUserFirst = sampleUserFirst.T
        pLs = []
        count = 0
        for i, j in zip(sampleUserFirst, sampleUserSecond):
            _, p_value = stats.ks_2samp(i, j)
            # print(p_value)
            pLs.append(p_value)
            count = count + 1 if p_value < config.DenyDomain else count
        return pLs, count / len(pLs)

    def EuclideanDistance(self, *another):
        for ano in another:
            if ano.Map is None:
                self.EachOneHotCoding(*another)
                break
        UserSampleDomain = []
        # UserSampleDomain = [random.sample(list(self.Map), config.SelfDeviationN)]
        for ano in another:
            UserSampleDomain.append(
                [np.array(random.sample(list(ano.Map), config.SelfDeviationN)) for i in range(config.SelfDeviationNUM)])
        UserSampleDomain = np.array(UserSampleDomain)
        E = []
        for i, user in enumerate(UserSampleDomain):
            E.append([])
            for j, samples_f in enumerate(user):
                for k, samples_l in enumerate(user):
                    if k <= j:
                        continue
                    E[-1].append([])
                    for p, (ef, el) in enumerate(zip(np.array(samples_f).T, np.array(samples_l).T)):
                        # print(np.sqrt((np.array(ef)-np.array(el))**2))
                        E[-1][-1].append(np.sqrt(np.sum((np.array(ef) - np.array(el)) ** 2)))
                        # input('wait')
        # print(np.array(E).shape)
        # print(E.shape)
        E = config.EWeight * 2 * np.sum(np.array(E) / (config.SelfDeviationN * (config.SelfDeviationN - 1)), axis=1)
        # # 用户 -- 特征高斯权重
        gaussWeights = np.array([np.exp(-(e - np.mean(e)) ** 2 / (2 * config.GaussianWeightingVar ** 2)) for e in E])
        # print(gaussWeights.shape)
        WE = []
        for i, user in enumerate(UserSampleDomain):
            WE.append([])
            for j, samples_f in enumerate(user):
                WE[-1].append([])
                for k, samples_l in enumerate(user):
                    if k == j:
                        continue
                    WE[-1][-1].append(0)
                    for p, (ef, el) in enumerate(zip(np.array(samples_f).T, np.array(samples_l).T)):
                        WE[-1][-1][-1] += np.sqrt(np.sum(gaussWeights[i][p] * (np.array(ef) - np.array(el)) ** 2))/config.SelfDeviationN
        # WE = np.sum(np.array(WE)/(config.SelfDeviationN*(config.SelfDeviationN-1)), axis=1)
        V = np.array([np.mean(we) for we in WE])
        StdDev = np.array([np.std(np.array(we).reshape(-1)) for we in WE])
        # # WE = np.sum(np.array(WE)/)
        # print(np.array(WE).shape)
        # V = [i for i in WE]
        return UserSampleDomain, gaussWeights, V, StdDev

    def Detection(self, USD, GW, V, StdDev):
        # 10,10,30,100
        # print(USD.shape)
        # 10, 100
        # print(GW.shape)
        # 10
        # print(V.shape)
        # 10
        # print(StdDev.shape)
        WE = []
        for i,userF in enumerate(USD):
            WE.append([])
            for j,userL in enumerate(USD):
                if i == j:
                    continue
                for samF in userF:
                    for samL in userL:
                        WE[-1].append(0)
                        for k,(vecF,vecL) in enumerate(zip(samL,samF)):
                            WE[-1][-1] += np.sum(np.sqrt(GW[i][k]*(vecL-vecF)**2))/config.SelfDeviationN
        # print(np.array(WE).shape)

        for v in config.u:
            best = 0
            for i, user in enumerate(WE):
                threshold = V[i]*v+StdDev[i]
                count = 0
                for j in user:
                    count = count+1 if j > threshold else count
                best += count/len(user)/len(WE)
            if best > config.AllowedFP:
                return v
        print('error')
        return -1


                # # print(um.ActivityInterval)

    def Matrix(self,*another,GW,V, StdDev,v):
        for ano in another:
            if ano.Map is None:
                self.EachOneHotCoding(*another)
                break
        UserSampleDomain = []
        # UserSampleDomain = [random.sample(list(self.Map), config.SelfDeviationN)]
        for ano in another:
            UserSampleDomain.append([random.sample(list(ano.Map), config.MatrixN) for i in range(config.IREALLYDONTKNOWWHATTHEHELL)])
        # print(np.array(UserSampleDomain).shape)
        # input('wait')
        UserSampleDomain = np.array(UserSampleDomain)
        UserSampleDomain = UserSampleDomain.reshape((config.SelfDeviationM*config.IREALLYDONTKNOWWHATTHEHELL,config.MatrixN,-1))
        WE = []
        for i, sdf in enumerate(UserSampleDomain):
            WE.append([])
            for j, sdl in enumerate(UserSampleDomain):
                WE[-1].append(0)
                for k, (vecf,vecl) in enumerate(zip(sdl,sdf)):
                    WE[-1][-1]+=np.sqrt(GW[int(i/config.IREALLYDONTKNOWWHATTHEHELL)][k]*np.sum((vecl-vecf)**2))/config.MatrixN
                # WE[-1].append(config.SelfDeviationNUM*config.SelfDeviationNUM*np.sum(np.sqrt((GW[int(i/config.IREALLYDONTKNOWWHATTHEHELL)])*(userF-userL)**2)))
        threshold = V + v*StdDev
        print(threshold)
        print(WE[0])
        dfm = [[1 if j > threshold[int(i/config.IREALLYDONTKNOWWHATTHEHELL)] else 0 for j in uod] for i, uod in enumerate(WE)]
        np.savetxt('混淆矩阵.csv', np.array(dfm),fmt='%d')
        # print(np.array(WE).shape)

# # print(um.Map)
# # np.setx('umMap.csv',um.Map)
# print(um.TransformKS(UserModel()))
# print(um.TransformKS(um))
um = UserModel()
uml = [UserModel() for i in range(config.SelfDeviationM)]
UserSampleDomain, gaussWeights, V, StdDev = um.EuclideanDistance(*uml)
v = um.Detection(UserSampleDomain, gaussWeights, V, StdDev)
um.Matrix(*uml,GW=gaussWeights, V=V, StdDev=StdDev, v=v)
